package topic

import (
	"context"
	"fmt"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/mqueuex"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"
	"mall/service/backend/common/utils/pointy"
	"mall/service/forum/model"
	"mall/service/forum/rpc/internal/svc"
	"mall/service/forum/rpc/internal/utils/errorhandler"
	"mall/service/forum/rpc/types/forum"
	"mall/service/user/rpc/userclient"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type BackendBatchUpdateTopicLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewBackendBatchUpdateTopicLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendBatchUpdateTopicLogic {
	return &BackendBatchUpdateTopicLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *BackendBatchUpdateTopicLogic) BackendBatchUpdateTopic(in *forum.BackendTopicListReq) (*forum.BaseResp, error) {
	logrus.Info(fmt.Sprintf("BackendBatchUpdateTopic BackendTopicListReq: %+v", in))

	if in.Ids == nil || len(strings.TrimSpace(*in.Ids)) == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	filterMap := make(map[string]map[string][]any)
	if vals := modelx.UniqueInterfaces(strings.TrimSpace(*in.Ids), 64); len(vals) > 0 {
		filterMap[model.ForumTopicColumns.TopicID] = map[string][]any{
			"in": vals,
		}
	} else {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	var status int32
	var sticky int32
	var noticeType int32
	var restricted bool // 数据有效
	var recFeed bool
	if in.Type != nil {
		switch int32(*in.Type) {
		case globalkey.ForumModTypeTopic: // 问题审核
			if in.Status != nil {
				switch int32(*in.Status) {
				case globalkey.StatusApproved: // 审核通过
					filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
						"in": []any{globalkey.StatusPending, globalkey.StatusCheckSuccess},
					}
					status = globalkey.StatusApproved
					noticeType = globalkey.ForumNoticeTypeApproved // 审核通过
					restricted = true
					recFeed = true
				case globalkey.StatusRejected: // 审核驳回
					filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
						"in": []any{globalkey.StatusPending, globalkey.StatusCheckSuccess, globalkey.StatusCheckFailed},
					}
					status = globalkey.StatusRejected
					noticeType = globalkey.ForumNoticeTypeRejected // 审核驳回
					restricted = true
				}
			}
		case globalkey.ForumModTypeTopicSticky: // 问题推荐
			if in.Sticky != nil {
				if *in.Sticky > 0 {
					filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
						"equalTo": []any{globalkey.StatusApproved},
					}
					filterMap[model.ForumTopicColumns.Sticky] = map[string][]any{
						"equalTo": []any{0},
					}
					sticky = globalkey.StatusSticky              // 推荐
					noticeType = globalkey.ForumNoticeTypeSticky // 推荐
					restricted = true
				} else {
					filterMap[model.ForumTopicColumns.TopicStatus] = map[string][]any{
						"equalTo": []any{globalkey.StatusApproved},
					}
					filterMap[model.ForumTopicColumns.Sticky] = map[string][]any{
						"equalTo": []any{1},
					}
					sticky = globalkey.StatusUnsticky              // 取消推荐
					noticeType = globalkey.ForumNoticeTypeUnsticky // 取消推荐
					restricted = true
				}
			}
		}
	}

	resp := &forum.BaseResp{}

	if !restricted {
		return resp, nil
	}

	res, _, err := l.svcCtx.Topic.GetList(l.ctx, model.M{}, -1, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if len(res) == 0 {
		return resp, nil
	}

	idTopicMap := make(map[uint64]*model.ForumTopic)
	idAuthorMap := make(map[uint64]uint64)
	var tids []uint64 // topic ids
	var ids []any
	for _, v := range res {
		idTopicMap[v.TopicID] = v
		idAuthorMap[v.TopicID] = v.TopicAuthorID
		tids = append(tids, v.TopicID)
		ids = append(ids, v.TopicID)
	}

	tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	now := time.Now() // 本地时间
	gmtNow := modelx.GetGmtTime(now)
	if status > 0 { // 审核
		logs := l.GetLogs(in, idTopicMap, *in.Type, status)
		notices := l.GetNotices(in, idTopicMap, noticeType)
		err = l.svcCtx.Topic.UpdateBatch(l.ctx, tx, tids, model.M{
			model.ForumTopicColumns.TopicStatus:       status,
			model.ForumTopicColumns.TopicModifiedDate: now,
			model.ForumTopicColumns.TopicModifiedGMT:  gmtNow,
		}, &model.ForumTopicRel{
			Logs:    logs,
			Notices: notices,
		})
		if err != nil {
			tx.Rollback()
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	} else { // 推荐
		logs := l.GetLogs(in, idTopicMap, *in.Type, sticky)
		notices := l.GetNotices(in, idTopicMap, noticeType)
		err = l.svcCtx.Topic.UpdateBatch(l.ctx, tx, tids, model.M{
			model.ForumTopicColumns.Sticky:            sticky,
			model.ForumTopicColumns.TopicModifiedDate: now,
			model.ForumTopicColumns.TopicModifiedGMT:  gmtNow,
		}, &model.ForumTopicRel{
			Logs:    logs,
			Notices: notices,
		})
		if err != nil {
			tx.Rollback()
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	}

	if recFeed { // 问题审核通过
		if in.Gems != nil && len(*in.Gems) > 0 {
			gem, err := moneyx.ParseGem(*in.Gems)
			if err != nil {
				logrus.Info(fmt.Sprintf("BackendBatchUpdateTopic failed to ParseGem err: %+v", err))
				tx.Rollback()
				return nil, err
			}

			var taskMeta []*userclient.UserMetaInfo
			for tid, authorId := range idAuthorMap {
				if topic, ok := idTopicMap[tid]; ok {
					taskMeta = append(taskMeta, &userclient.UserMetaInfo{
						Uid:   pointy.GetPointer(tid),
						Id:    pointy.GetPointer(authorId),
						Key:   pointy.GetPointer("任务奖励，发布问题:" + topic.Title),
						Value: pointy.GetPointer(topic.Slug),
					})
				}
			}
			if len(taskMeta) > 0 {
				_, err = l.svcCtx.UserRpc.SetMemberMeta(l.ctx, &userclient.SetMemberMetaReq{
					TaskName: pointy.GetPointer(mqueuex.UserTaskForumQuestion), // forum_topic
					TaskGem:  pointy.GetPointer(uint32(gem)),
					TaskMeta: taskMeta,
				})
				if err != nil {
					logrus.Info(fmt.Sprintf("BackendBatchUpdateTopic UserRpc.SetMemberMeta err: %+v", err))
					tx.Rollback()
					return nil, err
				}
			}
		}

		feedFilterMap := make(map[string]map[string][]any)
		feedFilterMap[model.ForumFeedColumns.TopicID] = map[string][]any{
			"in": ids,
		}
		feedFilterMap[model.ForumFeedColumns.Status] = map[string][]any{
			"equalTo": []any{globalkey.StatusPending},
		}
		list, _, err := l.svcCtx.Post.GetFeeds(l.ctx, model.M{}, -1, feedFilterMap)
		if err != nil {
			logrus.Info(fmt.Sprintf("BackendBatchUpdateTopic GetFeeds err: %+v", err))
		} else if len(list) > 0 {
			var fids []uint64
			for _, v := range list {
				fids = append(fids, v.ID)
			}

			_, err = l.svcCtx.Post.UpdateFeedBatch(l.ctx, tx, fids, model.M{
				model.ForumFeedColumns.Status: globalkey.StatusApproved,
			})
			if err != nil {
				tx.Rollback()
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}
		}
	}

	tx.Commit()

	return &forum.BaseResp{
		Msg: i18n.UpdateSuccess,
	}, nil
}

func (l *BackendBatchUpdateTopicLogic) GetLogs(
	in *forum.BackendTopicListReq,
	idTopicMap map[uint64]*model.ForumTopic,
	moderationType uint32,
	status int32,
) (res []*model.ForumTopicModerationLog) {
	now := time.Now()
	for _, v := range idTopicMap {
		var log model.ForumTopicModerationLog
		log.TopicAuthorID = v.TopicAuthorID
		log.ForumID = v.ForumID
		log.TopicID = v.TopicID
		log.Type = uint8(moderationType)
		log.Status = uint8(status)
		if in.Memo != nil {
			log.Memo = *in.Memo
		}
		if in.Extra != nil {
			log.Extra = *in.Extra
		}
		if in.UpdatedBy != nil {
			log.UpdatedBy = *in.UpdatedBy
		}
		log.UpdatedAt = now
		res = append(res, &log)
	}

	return
}

func (l *BackendBatchUpdateTopicLogic) GetNotices(
	in *forum.BackendTopicListReq,
	idTopicMap map[uint64]*model.ForumTopic,
	noticeType int32,
) (res []*model.ForumUserNotice) {
	var status uint8 = 1 // 未读
	now := time.Now()
	for _, v := range idTopicMap {
		var notice model.ForumUserNotice
		notice.UserID = v.TopicAuthorID
		notice.ForumID = v.ForumID
		notice.TopicID = v.TopicID
		notice.Type = uint16(noticeType)
		notice.Title = v.Title
		notice.Description = v.Slug
		if in.Memo != nil {
			notice.Memo = *in.Memo
		}
		if in.Extra != nil {
			notice.Extra = *in.Extra
		}
		notice.Status = status
		notice.CreatedAt = now
		notice.UpdatedAt = now
		res = append(res, &notice)
	}

	return
}
